package is.glif.fenius.webui.servlet;


import is.glif.fenius.webui.common.WebUIException;
import is.glif.fenius.webui.db.bean.WebReserveRequest;
import is.glif.fenius.webui.resv.Decomposer;
import is.glif.fenius.webui.topo.FileEdgeProvider;
import is.glif.fenius.webui.topo.FileNetworkProvider;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.Date;
import java.util.Enumeration;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.log4j.Logger;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

public class CreateServlet extends HttpServlet {
    private static Logger log = Logger.getLogger(CreateServlet.class);
    private static final long serialVersionUID = 1L;
    
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.setContentType("text/html");
        response.setStatus(HttpServletResponse.SC_OK);
        PrintWriter pw = response.getWriter();
        // pw.println("<h1>Hello from CreateServlet</h1>");
        // this.printParams(request, pw);
        String src = request.getParameter("src");
        String dst = request.getParameter("dst");
        String srcNet = request.getParameter("srcNet");
        String dstNet = request.getParameter("dstNet");
        String bandwidth = request.getParameter("bandwidth");
        String srcVlan = request.getParameter("srcVlan");
        String dstVlan = request.getParameter("dstVlan");
        String upMillis = request.getParameter("upMillis");
        String dnMillis = request.getParameter("dnMillis");
        
        log.debug("src: "+src);
        log.debug("dst: "+dst);
        log.debug("srcNet: "+srcNet);
        log.debug("dstNet: "+dstNet);
        log.debug("srcVlan: "+srcVlan);
        log.debug("dstVlan: "+dstVlan);
        log.debug("bw: "+bandwidth);
        log.debug("up: "+upMillis);
        log.debug("dn: "+dnMillis);
        Date upDt = new Date(Long.parseLong(upMillis));
        log.debug("up date: " + upDt);
        Date dnDt = new Date(Long.parseLong(dnMillis));
        log.debug("dn date: " + dnDt);
        
        try {
            this.validateBandwidth(bandwidth);
            this.validateEndpoints(srcNet, dstNet, src, dst);
            this.validateTime(upMillis, dnMillis);
            this.validateVlans(srcVlan, dstVlan);
        } catch (Exception ex) {
            pw.println("Error: "+ex.getMessage());
        }
        WebReserveRequest webreq = new WebReserveRequest();
        webreq.setSrc(src);
        webreq.setDst(dst);
        webreq.setSrcNet(srcNet);
        webreq.setDstNet(dstNet);
        webreq.setSrcVlan(Integer.parseInt(srcVlan));
        webreq.setDstVlan(Integer.parseInt(dstVlan));
        webreq.setUpMillis(Long.parseLong(upMillis));
        webreq.setDnMillis(Long.parseLong(dnMillis));
        webreq.setBandwidth(Integer.parseInt(bandwidth));
        webreq.setMonitor(true);
        
        this.printParams(request, pw);
        try {
            JSONObject obj = Decomposer.getInstance().reserve(webreq);
            
            String output;
            output = obj.toString(2);
            pw.println(output);
        } catch (JSONException e) {
            log.error(e);
            pw.println("Internal error");
        } catch (WebUIException e) {
            log.error(e);
            e.printStackTrace();
            pw.println("Internal error");
        }
    }
    
    private void validateEndpoints(String srcNet, String dstNet, String src, String dst) throws Exception {
        FileNetworkProvider np = FileNetworkProvider.getInstance();
        FileEdgeProvider ep = FileEdgeProvider.getInstance();
        JSONArray networks = null;
        try {
            networks = np.getNetworks("");
        } catch (JSONException e) {
            throw new Exception("Internal error loading networks");
        }
        if (networks == null || networks.length() == 0 ) {
            throw new Exception("Internal error loading networks");
        }
        boolean foundSrcNet = false;
        boolean foundDstNet = false;
        boolean foundSrc = false;
        boolean foundDst = false;
        for (int i = 0; i < networks.length(); i++) {
            JSONObject networkObj = networks.getJSONObject(i);
            String netId = networkObj.getString("id");
            if (netId.equals(srcNet)) foundSrcNet = true;
            if (netId.equals(dstNet)) foundDstNet = true;
            JSONArray edges = ep.getEdges(netId, "", "");
            for (int j = 0; j < edges.length(); j++) {
                JSONObject edgeObj = edges.getJSONObject(j);
                String urn = edgeObj.getString("urn");
                if (urn.equals(src)) foundSrc = true;
                if (urn.equals(dst)) foundDst = true;
            }
        }
        if (!foundSrcNet) throw new Exception("Invalid source network "+srcNet);
        if (!foundDstNet) throw new Exception("Invalid destination network "+dstNet);
        if (!foundSrc) throw new Exception("Invalid source endpoint "+src);
        if (!foundDst) throw new Exception("Invalid destination endpoint"+dst);

    }
    
    private void validateBandwidth(String bandwidth) throws Exception {
        if (bandwidth == null || bandwidth.equals("")) {
            throw new Exception("Bandwidth must be set.");
        }
        Integer iBandwidth;

        try {
            iBandwidth = Integer.parseInt(bandwidth);
        } catch (NumberFormatException e) {
           throw new Exception("Could not parse bandwidth.");
        }
        if (iBandwidth < 0 || iBandwidth > 10000) {
            throw new Exception("Source VLAN outside acceptable range 0-10000.");
        }
    }
    
    private void validateVlans(String srcVlan, String dstVlan) throws Exception {
        if (srcVlan == null || srcVlan.equals("")) {
            throw new Exception("Source VLAN must be set.");
        } else if (dstVlan == null || dstVlan.equals("")) {
            throw new Exception("Destination VLAN must be set.");
        } 
        Integer iSrcVlan;
        Integer iDstVlan;
        try {
             iSrcVlan = Integer.parseInt(srcVlan);
        } catch (NumberFormatException e) {
            throw new Exception("Could not parse source VLAN.");
        }
        try {
            iDstVlan = Integer.parseInt(dstVlan);
       } catch (NumberFormatException e) {
           throw new Exception("Could not parse destination VLAN.");
       }
       if (iSrcVlan < 2 || iSrcVlan > 4095) {
           throw new Exception("Source VLAN outside acceptable range 2-4095.");
       }
       if (iDstVlan < 2 || iDstVlan > 4095) {
           throw new Exception("Destination VLAN outside acceptable range 2-4095.");
       }
    }
    
    private void validateTime(String upMillis, String dnMillis) throws Exception {
        if (upMillis == null) {
            throw new Exception("Setup time must be set.");
        } else if (dnMillis == null) {
            throw new Exception("Teardown time must be set.");
        }
        Long up = null;
        Long dn = null;
        try {
            up = Long.parseLong(upMillis);
        } catch (NumberFormatException e) {
            throw new Exception("Could not parse setup time.");
        }
        try {
            dn = Long.parseLong(dnMillis);
        } catch (NumberFormatException e) {
            throw new Exception("Could not parse teardown time.");
        }
        Date now = new Date();
        if (up >= dn) {
            throw new Exception("Teardown time must be later than setup.");
        }
        if (up < now.getTime()) {
            throw new Exception("Setup time must be in the future.");
        }
    }
    
    @SuppressWarnings("rawtypes")
    private void printParams(HttpServletRequest request, PrintWriter pw) {
        pw.println("<h1>Hello from CreateServlet</h1>");
        
        pw.println("<table>");
        Enumeration paramNames = request.getParameterNames();
        while(paramNames.hasMoreElements()) {
            String paramName = (String)paramNames.nextElement();
            pw.println("<tr><td>" + paramName + "\n</td><td>");
            String[] paramValues = request.getParameterValues(paramName);
            if (paramValues.length == 1) {
                String paramValue = paramValues[0];
                if (paramValue.length() == 0)
                  pw.print("<I>No Value</I>");
                else
                  pw.print(paramValue);
              } else {
                pw.println("<ul>");
                for(int i=0; i<paramValues.length; i++) {
                  pw.println("<li>" + paramValues[i]+"</li>");
                }
                pw.println("</ul>");
              }
            pw.println("</td></tr>");
        }
        pw.println("</table>");
    }
}
